package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.heima.apis.article.IArticleClient;
import com.heima.apis.schedule.IScheduleClient;
import com.heima.common.aliyun.GreenImageScan;
import com.heima.common.aliyun.GreenTextScan;
import com.heima.common.exception.CustomException;
import com.heima.common.tess4j.Tess4jClient;
import com.heima.file.service.FileStorageService;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.common.enums.TaskTypeEnum;
import com.heima.model.schedule.dtos.Task;
import com.heima.model.wemedia.pojos.WmChannel;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmSensitive;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.model.wemedia.vo.ContentVO;
import com.heima.utils.common.ProtostuffUtil;
import com.heima.utils.common.SensitiveWordUtil;
import com.heima.utils.thread.WmThreadLocalUtil;
import com.heima.wemedia.entity.TextAndImage;
import com.heima.wemedia.mapper.ChannelMapper;
import com.heima.wemedia.mapper.NewsMapper;
import com.heima.wemedia.mapper.SentiveMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.AutoScanService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author brianxia
 * @version 1.0
 * @date 2022-11-14 10:42
 */
@Slf4j
@Service
public class AutoScanServiceImpl implements AutoScanService {

    @Autowired
    private GreenTextScan greenTextScan;
    @Autowired
    private GreenImageScan greenImageScan;
    @Autowired
    private FileStorageService fileStorageService;
    @Autowired
    private NewsMapper newsMapper;
    @Autowired
    private IArticleClient articleClient;
    @Autowired
    private WmUserMapper userMapper;
    @Autowired
    private ChannelMapper channelMapper;
    @Autowired
    private SentiveMapper sentiveMapper;
    @Autowired
    private Tess4jClient tess4jClient;
    @Autowired
    private IScheduleClient iScheduleClient;

    @PostConstruct //初始化会调用
    @Override
    public void init(){
        List<String> sensitives = sentiveMapper.selectList(Wrappers.<WmSensitive>lambdaQuery()
                        .select(WmSensitive::getSensitives)).stream().map(WmSensitive::getSensitives)
                .collect(Collectors.toList());
        //放到集合中
        SensitiveWordUtil.initMap(sensitives);
    }

    @Override
    @Async //让这个方法变成异步方法
    public void autoScan(WmNews wmNews) {
        System.out.println(Thread.currentThread().getName());

        //提取文本 提取图片
        //1.标题 内容 图片中的文字  2.图片 从minio中下载 放到list集合中 byte[]
        TextAndImage textAndImage = getTextAndImage(wmNews);

        //自管理敏感词审核
        Map<String, Integer> map =
                SensitiveWordUtil.matchWords(textAndImage.getText());
        if(!map.isEmpty()){
            updateStatus(wmNews, WmNews.Status.FAIL,"文本中存在敏感词!");
            return;
        }

        //1.文本审核
        boolean result = autoScanText(textAndImage.getText(),wmNews);
        if(!result){
            return;
        }
        //2.图片审核
        result =  autoScanImage(textAndImage.getImageBytes(),wmNews);
        if(!result){
            return;
        }

        //发送延迟消息
        Task task = new Task(TaskTypeEnum.NEWS_SCAN_TIME);
        task.setExecuteTime(wmNews.getPublishTime().getTime());
        task.setParameters(ProtostuffUtil.serialize(wmNews));
        iScheduleClient.addTask(task);

    }

    @Override
    @Scheduled(fixedRate = 1000L)
    public void poll(){
        ResponseResult responseResult = iScheduleClient.poll(TaskTypeEnum.NEWS_SCAN_TIME.getTaskType()
                , TaskTypeEnum.NEWS_SCAN_TIME.getPriority());
        if(responseResult != null && Objects.equals(responseResult.getCode(),
                AppHttpCodeEnum.SUCCESS.getCode()) && responseResult.getData() != null){
            //避免泛型转换出问题
            Task data = JSON.parseObject(JSON.toJSONString(responseResult.getData()), Task.class);
            byte[] parameters = data.getParameters();
            WmNews wmNews = ProtostuffUtil.deserialize(parameters, WmNews.class);
            publishArticle(wmNews);
        }
    }

    private void publishArticle(WmNews wmNews) {
        //3.发布文章
        ResponseResult resp = saveArticleApp(wmNews);
        if(resp != null && Objects.equals(resp.getCode(), AppHttpCodeEnum.SUCCESS.getCode())){
            //数据保存到数据库，设置状态为9成功处理
            Long articleId = (Long) resp.getData();
            wmNews.setArticleId(articleId);

            updateStatus(wmNews,WmNews.Status.PUBLISHED,"审核成功");
        }else{
            throw new CustomException(AppHttpCodeEnum.SERVER_ERROR);
        }
    }

    private ResponseResult saveArticleApp(WmNews wmNews) {

        ArticleDto articleDto = new ArticleDto();
        BeanUtils.copyProperties(wmNews,articleDto);

        articleDto.setId(wmNews.getArticleId());

        Integer userId = wmNews.getUserId(); //不能从线程上下文threadlocal中去拿
        articleDto.setAuthorId(Long.valueOf(userId));

        //作者名字查询一次
        WmUser wmUser = userMapper.selectById(userId);
        if(wmUser != null){
            articleDto.setAuthorName(wmUser.getName());
        }
        //查询频道名称
        WmChannel wmChannel = channelMapper.selectById(wmNews.getChannelId());
        if(wmChannel != null){
            articleDto.setChannelName(wmChannel.getName());
        }

        articleDto.setLayout(wmNews.getType());
        articleDto.setCreatedTime(new Date());


        return articleClient.saveArticle(articleDto);
    }

    private boolean autoScanImage(List<byte[]> imageBytes, WmNews wmNews) {

        if(CollectionUtils.isEmpty(imageBytes)){
            return true;//没有图片，不需要审核
        }

        try{
            Map map = greenImageScan.imageScan(imageBytes);
            if(map != null){
                String suggestion = (String) map.get("suggestion");

                if(Objects.equals(suggestion,"pass")){
                    //成功
                    return true;
                }else if(Objects.equals(suggestion,"review")){
                    updateStatus(wmNews, WmNews.Status.ADMIN_AUTH, "文章图片中可能包含违规内容，需要人工审核");
                    //转人工
                    return false;
                }else if(Objects.equals(suggestion,"block")){
                    updateStatus(wmNews, WmNews.Status.FAIL, "文章图片中包含违规内容");
                    //拒绝
                    return false;
                }
            }

        }catch (Exception e){
            log.error("调用阿里云接口失败",e);
        }

        updateStatus(wmNews, WmNews.Status.ADMIN_AUTH, "文章图片中可能包含违规内容，需要人工审核");
        return false;
    }

    private boolean autoScanText(String text, WmNews wmNews) {

        try{
            Map map = greenTextScan.greeTextScan(text);
            if(map != null){
                String suggestion = (String) map.get("suggestion");

                if(Objects.equals(suggestion,"pass")){
                    //成功
                    return true;
                }else if(Objects.equals(suggestion,"review")){
                    updateStatus(wmNews, WmNews.Status.ADMIN_AUTH, "文章内容中可能包含敏感词，需要人工审核");
                    //转人工
                    return false;
                }else if(Objects.equals(suggestion,"block")){
                    updateStatus(wmNews, WmNews.Status.FAIL, "文章内容中包含敏感词");
                    //拒绝
                    return false;
                }
            }

        }catch (Exception e){
            log.error("调用阿里云接口失败",e);
        }

        updateStatus(wmNews, WmNews.Status.ADMIN_AUTH, "文章内容中可能包含敏感词，需要人工审核");
        return false;
    }

    private void updateStatus(WmNews wmNews, WmNews.Status adminAuth, String reason) {
        wmNews.setStatus(adminAuth.getCode());
        wmNews.setReason(reason);
        newsMapper.updateById(wmNews);
    }

    private TextAndImage getTextAndImage(WmNews wmNews) {

        //1.提取内容
        String content = wmNews.getContent();
        List<byte[]> imageBytes = new ArrayList<>();
        Set<String> urls = new HashSet<>();
        StringBuilder builder = new StringBuilder();
        //json反序列化 还原成对象
        List<ContentVO> contentVOS = JSON.parseArray(content, ContentVO.class);
        for (ContentVO contentVO : contentVOS) {
            if(Objects.equals(contentVO.getType(),"image")){
                //图片
                urls.add(contentVO.getValue());
            }else if(Objects.equals(contentVO.getType(),"text")){
                //文本
                builder.append(contentVO.getValue());
                builder.append("-");
            }
        }

        //标题
        String title = wmNews.getTitle();
        builder.append(title);
        builder.append("-");

        //封面
        String images = wmNews.getImages();//a.jpg,b.jpg
        if(StringUtils.isNotBlank(images)){
            String[] split = images.split(",");
            urls.addAll(Arrays.asList(split));
        }

        //图片特殊处理 拿到图片数据 + 提取图片中的文本
        try{
            for (String url : urls) {
                byte[] bytes = fileStorageService.downLoadFile(url);
                imageBytes.add(bytes);
                //提取图片中的文字
                BufferedImage read = ImageIO.read(new ByteArrayInputStream(bytes));
                String s = tess4jClient.doOCR(read);
                if(StringUtils.isNotBlank(s)){
                    builder.append(s);
                    builder.append("-");
                }
            }
        }catch (Exception e){
            log.error("图片下载或者提取文字失败!" + wmNews.getId(),e);
            throw new CustomException(AppHttpCodeEnum.SERVER_ERROR);
        }

        TextAndImage textAndImage = new TextAndImage();
        textAndImage.setText(builder.toString());
        textAndImage.setImageBytes(imageBytes);
        return  textAndImage;
    }

    public static void main(String[] args) {
        Task task = new Task();
        task.setTaskId(1L);

        ResponseResult responseResult = ResponseResult.okResult(task);

        //通过网络传输
        String s = JSON.toJSONString(responseResult);

        //还原成对象
        ResponseResult responseResult1 = JSON.parseObject(s, ResponseResult.class);

        //序列化
        Task task1 = JSON.parseObject(JSON.toJSONString(responseResult1.getData()), Task.class);

        System.out.println(task1);
    }

}
